#pragma once

#include <cstddef>

namespace ezstl {

namespace {
inline std::size_t ualigned_load(const char *p) {
  std::size_t result;
  __builtin_memcpy(&result, p, sizeof(result));
  return result;
}

inline std::size_t load_bytes(const char *p, int n) {
  std::size_t result = 0;
  --n;
  do result = (result << 8) + static_cast<unsigned char>(p[n]);
  while (--n >= 0);
  return result;
}

inline std::size_t shift_mix(std::size_t v) { return v ^ (v >> 47); }

}  // namespace

std::size_t hash_bytes(const void *ptr, std::size_t len, std::size_t seed) {
  static const size_t mul =
      (((size_t)0xc6a4a793UL) << 32UL) + (std::size_t)0x5bd1e995UL;
  const char *const buf = static_cast<const char *>(ptr);

  const std::size_t len_aligned = len & ~(std::size_t)0x7;
  const char *const end = buf + len_aligned;
  size_t hash = seed ^ (len * mul);
  for (const char *p = buf; p != end; p += 8) {
    const std::size_t data = shift_mix(ualigned_load(p) * mul) * mul;
    hash ^= data;
    hash *= mul;
  }

  if ((len & 0x7) != 0) {
    hash ^= shift_mix(load_bytes(end, len & 0x7) * mul);
    hash ^= mul;
    hash *= mul;
  }
  hash = shift_mix(hash) * mul;
  hash = shift_mix(hash);
  return hash;
}

struct hash_impl {
  static std::size_t hash(
      const void *ptr, std::size_t clength,
      size_t seed = static_cast<std::size_t>(0xc70f6907UL)) {
    return hash_bytes(ptr, clength, seed);
  }

  template <typename T>
  static std::size_t hash(const T &val) {
    return hash(&val, sizeof(val));
  }
};

template <typename Key>
struct hash {};

inline std::size_t __stl_hash_string(const char *s) {
  std::size_t h = 0;
  for (; *s; ++s) h = 5 * h + *s;
  return h;
}

template <>
struct hash<char *> {
  std::size_t operator()(const char *s) const { return __stl_hash_string(s); }
};

template <>
struct hash<const char *> {
  std::size_t operator()(const char *s) const { return __stl_hash_string(s); }
};

#define trivial_hash(T)                     \
  template <>                               \
  struct hash<T> {                          \
    std::size_t operator()(T val) const {   \
      return static_cast<std::size_t>(val); \
    }                                       \
  }

trivial_hash(bool);
trivial_hash(char);
trivial_hash(signed char);
trivial_hash(unsigned char);
trivial_hash(short);
trivial_hash(unsigned short);
trivial_hash(int);
trivial_hash(unsigned int);
trivial_hash(long);
trivial_hash(unsigned long);
trivial_hash(long long);
trivial_hash(unsigned long long);
#undef trivial_hash

template <>
struct hash<float> {
  std::size_t operator()(float val) {
    return val != 0.f ? hash_impl::hash(val) : 0;
  }
};

template<>
struct hash<double> {
  std::size_t operator()(double val) {
    return val != 0. ? hash_impl::hash(val) : 0;
  }
};



}  // namespace ezstl